#include "neighbours.h"
#include "pf_net_mod.h"
#include "handling.h"
#define MODULE_NAME "pf_net_mod"

/*
v strukture pf_net_struct definujem vsetky globalne premenne, ktore pouzivam
tato struktura je potom deklarovana ako pf_net
pf_run = ak je 1 prijimam dokola, ak 0 po najblizsom nacitani spravy skonci nacitavanie
pf_wait = moja pomocna premenna, nedolezite, teda kym nezacnete upravovat
pf_thread =  vlakno, ktore prijima data
pf_s_rec = socket pre nacitavanie
pf_s_bc = socket na broadcasty, na datove spravy sa vzdy inicializuje socket vo funkcii odosielania
pf_sadd_bc/rec - struktury definujuce port prijimania/odosielania (zavisi ci volame funkciu odosielania alebo prijimania, z toho pohladu to potom bere, taktiez definuje IP adresy (rovnaky pristup)
*/

struct pf_net_struct *pf_net = NULL;
int pf_ip_bc[] = { NODE_IPS };

int pf_receive(struct socket *s, struct sockaddr_in *addr, unsigned char *buf, int len);
int pf_send(struct socket *s, struct sockaddr_in *addr, unsigned char *buf, int len);


int pf_send_hello( char *buf, int len ) {
	naplanujOdoslanieHello( buf, len );
	return 0;
}

int pf_send_hello_workqueue(char *buf, int len) {
	int i, error;
	if (pf_net->pf_wait == 1)
	{
		for(i=0; i<ADDRNUM; i++)
		{
			error = pf_send(pf_net->pf_s_bc, &pf_net->pf_sadd_bc[i], buf, len);
			if (error < 0)
			{
				printk(KERN_INFO"pf_send_hello: error sending hello, index: %d, error: %d, addr: %x\n", i, error, pf_net->pf_sadd_bc[i].sin_addr.s_addr);
				return error;
			}
			printk(KERN_INFO"pf_send_hello: hello sent to address %d, addr: %x\n", i, pf_net->pf_sadd_bc[i].sin_addr.s_addr);	
		}
		return 0;	
	}
	else{
		return -1;
	}
}

int pf_send_data(uint32_t dest, char *buf, int len) {
	naplanujOdoslaniePDU(dest, buf, len);
	return 1;
}


int pf_send_data_workqueue(uint32_t dest, char *buf, int len)
{
		int error;
		struct sockaddr_in pf_sadd_out;
		struct socket *pf_s_out=NULL;
	
		printk(KERN_INFO"pf_send_data: destination received %x\n", dest);	
		if ( (error = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &pf_s_out)) < 0)
		{
				printk(KERN_INFO MODULE_NAME"pf_send_data: DBG: Could not create socket for data, error = %d\n", -ENXIO);
				return -1;
		}
		memset(&pf_sadd_out, 0, sizeof(struct sockaddr));
		pf_sadd_out.sin_family = AF_INET;
		pf_sadd_out.sin_addr.s_addr = dest;
		pf_sadd_out.sin_port = htons(PORT);

		error = pf_send(pf_s_out, &pf_sadd_out, buf, len);
		if (error < 0)
		{
			printk(KERN_INFO"pf_send_data: Sending data failed, error: %d\n", error);
		}
		else
		{
			printk(KERN_INFO"pf_send_data: Sending data successful, bytes of data: %d\n", error);
		}
		sock_release(pf_s_out);
		//kfree(pf_s_out);
		return error;
}

static void pf_start(void)
{
	int size;
        int bufsize = 1400;
        unsigned char *buf;
//	mm_segment_t oldfs;

        lock_kernel();
        current->flags |= PF_NOFREEZE;

        daemonize(MODULE_NAME);
        unlock_kernel();

	phs_debug(DBG_IP, "PF_NET: zacalo pf_start");

//	oldfs = get_fs();
//	set_fs(KERNEL_DS);

        while(pf_net->pf_run) {
                struct sockaddr_in addr;
		buf = (unsigned char *) vmalloc(bufsize);
		if (buf == NULL) {
			printk("nevytvorili sme si zasobnik pre prijati pre paket\n");
			phs_debug(DBG_IP, "DBG: receive_kthread -> nevytvorili sme si zasobnik pre prijati paket\n");
			continue;
		}
		else {
			memset(buf, 0, bufsize);
			size = pf_receive(pf_net->pf_s_rec, &addr, buf, bufsize);

			//check if process witch called function was not closed
			if (signal_pending(current)) {
                        	break;
			}
			if (size == 4) {
				continue;
			}
                	if (size < 0) {
				if (size == -ECONNRESET) {
                   			printk(KERN_INFO ": receving packet error, sock reset by peer\n");
				}
      	      			if (size != -ERESTARTSYS) {
					printk(KERN_INFO ": sock_recvmsg vratilo %d\n", size);
				}
				break;
			}
			if (size == 0) {
				printk(KERN_INFO ": sock bol vypnuty pouzivatelom\n");
				break;
			}
 			else {
				if (addr.sin_addr.s_addr != pf_net->ladr) {
			/*
			tu sa bude volat funkcia na spracovanie dat, budes potrebovat asi nasledovne parametre
			buf - prijate data
			budsize - data
			addr.sin_addr.s_addr - u_int32t hodnota zdrojovej adresy
			*/
					phs_debug(DBG_IP, "PHS: Volame switch handler");
				//	switch_handler(addr.sin_addr.s_addr, buf);
					printk("\npf_start: received packet: S_IP: size: %d, %d;\n", addr.sin_addr.s_addr, size);
					break;
				}
				else {
					printk(KERN_INFO"pf_start: packet received with my source IP");
					break;
				}
            		}
		}
        }
	pf_net->pf_wait = 0;
//	set_fs(oldfs);
}

int pf_send(struct socket *s, struct sockaddr_in *addr, unsigned char *buf, int len)
{
        struct msghdr msg;
        struct iovec iov;
        mm_segment_t oldfs;
        int size = 0;

        if (s->sk==NULL)
           return 0;

        iov.iov_base = buf;
        iov.iov_len = len;

        msg.msg_flags = 0;
        msg.msg_name = addr;
        msg.msg_namelen  = sizeof(struct sockaddr_in);
        msg.msg_control = NULL;
        msg.msg_controllen = 0;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_control = NULL;

        oldfs = get_fs();
	printk(KERN_INFO "pf_send: before send function, dest: %x\n", addr->sin_addr.s_addr);
        set_fs(KERNEL_DS);
        size = sock_sendmsg(s,&msg,len);
        set_fs(oldfs);

	printk(KERN_INFO"pf_send: after send function, dest: %x\n", addr->sin_addr.s_addr);
        return size;
}

int pf_receive(struct socket* s, struct sockaddr_in* addr, unsigned char* buf, int len)
{
        struct msghdr msg;
        struct iovec iov;
        mm_segment_t oldfs;
        int size = 0;

	phs_debug(DBG_IP, "DBG: pf_receive zacalo\n");
        if (s->sk==NULL) return 0;

        iov.iov_base = buf;
        iov.iov_len = len;

        msg.msg_flags = MSG_WAITALL | MSG_NOSIGNAL;
        msg.msg_name = addr;
        msg.msg_namelen  = sizeof(struct sockaddr_in);
        msg.msg_control = NULL;
        msg.msg_controllen = 0;
        msg.msg_iov = &iov;
        msg.msg_iovlen = 1;
        msg.msg_control = NULL;

        oldfs = get_fs();
        set_fs(KERNEL_DS);
        size = sock_recvmsg(s,&msg,len,msg.msg_flags);
        set_fs(oldfs);

        return size;
}

int __init pf_s_init(void)
{
		int error, i;
	struct net_device *dev;
	struct in_device *in_dev;	

	pf_net = kmalloc(sizeof(struct pf_net_struct), GFP_KERNEL);
	if (pf_net == NULL) {
		printk(KERN_ERR "DBG: pf_net struktura neinicializovana!!!\n");
		return -1;
	}
        memset(pf_net, 0, sizeof(struct pf_net_struct));
        
	pf_net->pf_run = 1;
	pf_net->pf_wait = 1;
		
	if ( (error = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &pf_net->pf_s_rec)) < 0){
                printk(KERN_INFO MODULE_NAME": Could not create receving socket, error = %d\n", -ENXIO);
                goto out;
        }
	if ( (error = sock_create(AF_INET, SOCK_DGRAM, IPPROTO_UDP, &pf_net->pf_s_bc)) < 0 ){
		printk(KERN_INFO MODULE_NAME"pf_init: Could not create broadcasting socket, error = %d\n", -ENXIO);
		goto release_rec;
	}
        memset(&pf_net->pf_sadd_rec, 0, sizeof(struct sockaddr));
        pf_net->pf_sadd_rec.sin_family = AF_INET;
        pf_net->pf_sadd_rec.sin_addr.s_addr = htonl(INADDR_ANY);
        pf_net->pf_sadd_rec.sin_port  = htons(PORT);


	pf_net->pf_sadd_bc = kmalloc((sizeof(pf_ip_bc) * ADDRNUM), GFP_KERNEL); 
	for (i=0; i<ADDRNUM; i++)
	{
		memset(&pf_net->pf_sadd_bc[i], 0, sizeof(struct sockaddr));
        	pf_net->pf_sadd_bc[i].sin_family      = AF_INET;
        	pf_net->pf_sadd_bc[i].sin_port = htons(PORT);
        	pf_net->pf_sadd_bc[i].sin_addr.s_addr = htonl(pf_ip_bc[i]);
	}

	if  ( (error = pf_net->pf_s_rec->ops->bind(pf_net->pf_s_rec, (struct sockaddr *)&pf_net->pf_sadd_rec, sizeof(struct sockaddr) ) ) < 0)
	{
		printk(KERN_INFO"pf_init: could not bind pf_s_rec, error = %d\n", -error);
		goto release_socks;
	}

	rtnl_lock();
	dev = dev_get_by_name(&init_net, PF_DEV);
	rtnl_unlock();

	if (dev == NULL)
	{
		printk(KERN_INFO"pf_s_init: dev NULL\n");
		goto release_socks;
	}
	in_dev = in_dev_get(dev);
	pf_net->ladr = in_dev->ifa_list->ifa_local;
	phs_initSourceIP(pf_net->ladr);


        pf_net->pf_thread = kthread_run((void *)pf_start, NULL, MODULE_NAME);
        if (IS_ERR(pf_net->pf_thread))
        {
		printk(KERN_INFO"pf_init: thread create unsuccessful\n");
		goto release_socks;
        }

	printk(KERN_INFO"pf_init: init successful\n");
	return 0;

release_socks:
	sock_release(pf_net->pf_s_bc);
        pf_net->pf_s_bc = NULL;
release_rec:
        sock_release(pf_net->pf_s_rec);
        pf_net->pf_s_rec = NULL;

out:
		pf_net->pf_wait = 0;
        pf_net->pf_thread = NULL;
        return -1;
}
void __exit pf_s_exit(void) {
        if( pf_net->pf_thread == NULL ) {
                printk(KERN_INFO MODULE_NAME"pf_exit: I have nothing to kill :(\n");
        }
	else {
		pf_net->pf_run = 0;
		pf_send_data_workqueue(htonl(INADDR_LOOPBACK), "END", 4);
		while (pf_net->pf_wait)	{
			msleep(500);
		}

		sock_release(pf_net->pf_s_bc);
		sock_release(pf_net->pf_s_rec);
		pf_net->pf_s_bc = NULL;
		pf_net->pf_s_rec = NULL;
		pf_net->pf_thread = NULL;
		printk(KERN_INFO"pf_exit: Thread for receiving finished\n");
        }
	kfree(pf_net->pf_sadd_bc);
        kfree(pf_net);
        pf_net = NULL;

        printk(KERN_INFO MODULE_NAME"pf_exit: module unloaded\n");
}

